package com.gitee.l0km.aocache;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;

import com.gitee.l0km.aocache.DataProtections.IdentityFilter;
import com.google.common.base.Throwables;
import com.google.common.reflect.TypeToken;

import static java.lang.reflect.Modifier.FINAL;
import static java.lang.reflect.Modifier.STATIC;
import static com.google.common.base.Preconditions.checkNotNull;


/**
 * DeepCloneUtils 类提供了一系列用于深度克隆对象的工具方法。
 * @author guyadong
 */
public class DeepCloneUtils {

	/**
	 * 对输入对象的单个字段进行浅拷贝操作。<p>
	 * 该方法会根据字段的属性（如是否为合成字段、静态字段、final字段、volatile字段等）进行不同的处理：<br>
	 * <li>忽略合成字段、静态字段和final字段。</li>
	 * <li>如果字段是基本类型或者不可变类型，则直接复制值。</li>
	 * <li>如果字段不是{@code volatile}类型，则对字段值进行浅拷贝。</li>
	 * <li>如果字段是{@code volatile}类型，根据{@code ifVolatile}的值进行处理：</li>
	 *   <ul>
	 *     <li>当{@code ifVolatile}为1时，将字段值置为{@code null}。</li>
	 *     <li>当{@code ifVolatile}为2时，对字段值进行浅拷贝。</li>
	 *     <li>当{@code ifVolatile}为3时，保持原字段值。</li>
	 *     <li>其他情况下，不进行任何操作。</li>
	 *   </ul>
	 * 
	 * @param input 要进行浅拷贝字段的输入对象
	 * @param field 要处理的字段
	 * @param ifVolatile 处理{@code volatile}字段的模式，1表示置为{@code null}，2表示浅拷贝
	 */
	public static void cloneField(Object input, Field field, int ifVolatile) {
		if (null == input) {
			return;
		}
		try {
			/** 忽略合成字段 */
			if (field.isSynthetic()) {
				return;
			}
			int modifiers = field.getModifiers();
			/** 忽略静态字段和final字段 */
			if ((modifiers & (STATIC + FINAL)) != 0) {
				return;
			}
			if (!Modifier.isPublic(modifiers)) {
				field.setAccessible(true);
			}
			Class<?> fieldType = field.getType();
			if (field.getType().isPrimitive() || DataProtections.isImmutable(fieldType)) {
				/** DO NOTHING */
			} else if (!Modifier.isVolatile(modifiers)) {
				/** 非volatile字段进行浅拷贝 */
				field.set(input, DataProtections.shallowClone(field.get(input), fieldType));
			} else {
				switch (ifVolatile) {
				case 1:
					// 模式1：将volatile字段置为null
					field.set(input, null); 
					break;
				case 2:
					// 模式2：浅拷贝volatile字段
					field.set(input, DataProtections.shallowClone(field.get(input), fieldType)); 
					break;
				case 3:
					// 模式3：保持原字段值
					/** DO NOTHING */ 
					break;
				default:
					// 其他情况不进行任何操作
					break;
				}
			}
		} catch (IllegalArgumentException | IllegalAccessException e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
	/**
	 * 根据字段名对输入对象的单个字段进行浅拷贝操作。<p>
	 * 若输入对象为{@code null}，则不进行任何操作。<br>
	 * 该方法会尝试通过输入对象的类获取指定名称的字段，然后调用{@link #cloneField(Object, Field, int)}方法进行浅拷贝操作。<br>
	 * 若指定名称的字段不存在或存在安全异常，会将异常转换为运行时异常抛出。<br>
	 * 
	 * @param input 要进行浅拷贝字段的输入对象
	 * @param fieldName 要处理的字段名称，不能为{@code null}
	 * @param ifVolatile 处理{@code volatile}字段的模式，1表示置为{@code null}，2表示浅拷贝
	 */
	public static void cloneField(Object input, String fieldName, int ifVolatile) {
		if (null == input) {
			return;
		}
		try {
			Field field = input.getClass().getDeclaredField(checkNotNull(fieldName, "fieldName is null"));
			cloneField(input, field, ifVolatile);
		}catch (NoSuchFieldException | SecurityException e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
	/**
	 * (浅拷贝)对输入对象的所有字段进行浅拷贝操作。<p>
	 * 该方法会遍历输入对象的所有非合成、非静态、非final字段，根据字段类型和配置进行不同的处理：<br>
	 * <li>如果字段是基本类型或者不可变类型，则直接复制值。</li>
	 * <li>如果字段不是{@code volatile}类型，则对字段值进行浅拷贝。</li>
	 * <li>如果字段是{@code volatile}类型，根据{@code ifVolatile}的值进行处理：</li>
	 *   <ul>
	 *     <li>当{@code ifVolatile}为1时，将字段值置为{@code null}。</li>
	 *     <li>当{@code ifVolatile}为2时，对字段值进行浅拷贝。</li>
	 *     <li>当{@code ifVolatile}为3时，保持原字段值。</li>
	 *     <li>其他情况下，不进行任何操作。</li>
	 *   </ul>
	 * 
	 * @param <T> 输入对象的类型
	 * @param input 要进行浅拷贝字段的输入对象
	 * @param inputType 输入对象的类型信息，不能为{@code null}
	 * @param ifVolatile 处理{@code volatile}字段的模式，1表示置为{@code null}，2表示浅拷贝
	 * @return 处理后的输入对象，如果输入为{@code null}，则返回{@code null}
	 */
	public static <T> T cloneFields(T input, Class<T> inputType, int ifVolatile) {
		if (null == input) {
			return null;
		}
		if (DataProtections.isImmutable(input)) {
			/** 不可变类型不需要复制 */
			return input;
		}
		for (Class<?> clazz = input.getClass(); null != clazz
				&& !Object.class.equals(clazz); clazz = clazz.getSuperclass()) {
			for (Field field : clazz.getDeclaredFields()) {
				cloneField(input, field, ifVolatile);
			}
		}
		return input;
	}
	/**
	 * (深度克隆)对数据对象的所有字段执行深度复制,输入参数为{@code null}返回{@code null}<br>
	 * @see #cloneFields(Object, Class, int)
	 */
	@SuppressWarnings("unchecked")
	public static <T>T cloneFields(T input){
		return null == input ? null : cloneFields(input, (Class<T>)input.getClass(), 2);
	}
	/**
	 * 对输入对象的单个字段进行深度克隆操作。<p>
	 * 该方法会根据字段的属性（如是否为合成字段、静态字段、final字段、volatile字段等）进行不同的处理：<br>
	 * <li>忽略合成字段、静态字段和final字段。</li>
	 * <li>如果字段经过{@code identityFilter}验证为不可变类型，则直接复制值。</li>
	 * <li>如果字段不是{@code volatile}类型，则对字段值进行深度克隆。</li>
	 * <li>如果字段是{@code volatile}类型，根据{@code ifVolatile}的值进行处理：</li>
	 *   <ul>
	 *     <li>当{@code ifVolatile}为1时，将字段值置为{@code null}。</li>
	 *     <li>当{@code ifVolatile}为2时，对字段值进行深度克隆。</li>
	 *     <li>当{@code ifVolatile}为3时，保持原字段值。</li>
	 *     <li>其他情况下，不进行任何操作。</li>
	 *   </ul>
	 * 
	 * @param input 要进行深度克隆字段的输入对象
	 * @param inputType 输入对象的类型信息，如果为{@code null}，则使用输入对象的实际类型
	 * @param field 要处理的字段
	 * @param ifVolatile 处理{@code volatile}字段的模式，1表示置为{@code null}，2表示深度克隆
	 * @param identityFilter 用于过滤不可变类型的过滤器
	 */
	@SuppressWarnings("unchecked")
	public static void deepCloneField(Object input, Class<?> inputType, Field field, int ifVolatile, IdentityFilter identityFilter) {
		if (null == input) {
			return;
		}
		if(null == inputType) {
			inputType = input.getClass();
		}
		try {
			/** 忽略合成字段 */
			if (field.isSynthetic()) {
				return;
			}
			int modifiers = field.getModifiers();
			/** 忽略静态字段和final字段 */
			if ((modifiers & (STATIC + FINAL)) != 0) {
				return;
			}
			if (!Modifier.isPublic(modifiers)) {
				field.setAccessible(true);
			}
			TypeToken<Object> resolvedToken = (TypeToken<Object>) AocacheUtils.resolveTypeToken(inputType,
					field.getGenericType());
			if (identityFilter.identity(resolvedToken)) {
				/** DO NOTHING */
			} else if (!Modifier.isVolatile(modifiers)) {
				/** 非volatile字段进行深度克隆 */
				field.set(input, DataProtections.deepClone(field.get(input), resolvedToken, identityFilter));
			} else {
				switch (ifVolatile) {
				case 1:
					// 模式1：将volatile字段置为null
					field.set(input, null);
					break;
				case 2:
					// 模式2：深度克隆volatile字段
					field.set(input, DataProtections.deepClone(field.get(input), resolvedToken, identityFilter));
					break;
				case 3:
					// 模式3：保持原字段值
					/** DO NOTHING */
					break;
				default:
					// 其他情况不进行任何操作
					break;
				}
			}
		} catch (IllegalArgumentException | IllegalAccessException e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
	/**
	 * 根据字段名对输入对象的单个字段进行深度克隆操作。<p>
	 * 若输入对象为{@code null}，则不进行任何操作。<br>
	 * 该方法会尝试通过输入对象的类获取指定名称的字段，然后调用{@link #deepCloneField(Object, Class, Field, int, DataProtections.IdentityFilter)}方法进行深度克隆操作。<br>
	 * 若指定名称的字段不存在或存在安全异常，会将异常转换为运行时异常抛出。<br>
	 * 
	 * @param input 要进行深度克隆字段的输入对象
	 * @param inputType 输入对象的类型信息，如果为{@code null}，则使用输入对象的实际类型
	 * @param fieldName 要处理的字段名称，不能为{@code null}
	 * @param ifVolatile 处理{@code volatile}字段的模式，1表示置为{@code null}，2表示深度克隆
	 * @param identityFilter 用于过滤不可变类型的过滤器
	 */
	public static void deepCloneField(Object input, Class<?> inputType, String fieldName, int ifVolatile, IdentityFilter identityFilter) {
		if (null == input) {
			return;
		}
		try {
			Field field = input.getClass().getDeclaredField(checkNotNull(fieldName, "fieldName is null"));
			deepCloneField(input, inputType, field, ifVolatile, identityFilter);
		} catch (NoSuchFieldException | SecurityException e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * (深度克隆)对输入对象的所有字段进行深度克隆操作。<p>
	 * 若输入对象为{@code null}，则直接返回{@code null}。<br>
	 * 若输入对象为不可变类型，则直接返回原对象，无需复制。<br>
	 * 该方法会遍历输入对象及其所有父类的所有非合成、非静态、非final字段，<br>
	 * 根据字段类型、是否为{@code volatile}字段以及{@code ifVolatile}参数的值进行不同的处理：<li>
	 * <li>如果字段经过{@code identityFilter}验证为不可变类型，则直接复制值。</li>
	 * <li>如果字段不是{@code volatile}类型，则对字段值进行深度克隆。</li>
	 * <li>如果字段是{@code volatile}类型，根据{@code ifVolatile}的值进行处理：</li>
	 *   <ul>
	 *     <li>当{@code ifVolatile}为1时，将字段值置为{@code null}。</li>
	 *     <li>当{@code ifVolatile}为2时，对字段值进行深度克隆。</li>
	 *     <li>当{@code ifVolatile}为3时，保持原字段值。</li>
	 *     <li>其他情况下，不进行任何操作。</li>
	 *   </ul>
	 * </li>
	 * 
	 * @param <T> 输入对象的类型
	 * @param input 要进行深度克隆字段的输入对象
	 * @param inputType 输入对象的类型信息，不能为{@code null}
	 * @param ifVolatile 处理{@code volatile}字段的模式，1表示置为{@code null}，2表示深度克隆
	 * @param identityFilter 用于过滤不可变类型的过滤器
	 * @return 处理后的输入对象，如果输入为{@code null}，则返回{@code null}
	 */
	public static <T> T deepCloneFields(T input, Class<T> inputType, int ifVolatile, IdentityFilter identityFilter) {
		if (null == input) {
			return null;
		}
		if (DataProtections.isImmutable(input, checkNotNull(inputType, "inputType is null"))) {
			/** 不可变类型不需要复制 */
			return input;
		}
		for (Class<?> clazz = input.getClass(); null != clazz
				&& !Object.class.equals(clazz); clazz = clazz.getSuperclass()) {
			for (Field field : clazz.getDeclaredFields()) {
				deepCloneField(input, clazz, field, ifVolatile, identityFilter);
			}
		}
		return input;
	}

	/**
	 * (深度克隆)对输入对象进行深度复制，输入参数为{@code null}时返回{@code null}。<p>
	 * 该方法会对输入对象的所有字段执行深度复制操作。<br>
	 * 
	 * @param <T> 输入对象的类型
	 * @param input 要进行深度克隆的输入对象
	 * @param inputType 输入对象的类类型信息
	 * @param identityFilter 用于过滤不可变类型的过滤器
	 * @return 深度克隆后的对象，如果输入为{@code null}，则返回{@code null}
	 */
	public static <T> T deepClone(T input, Class<T> inputType, IdentityFilter identityFilter){
		return DataProtections.deepClone(input, inputType, identityFilter);
	}

	/**
	 * (深度克隆)对输入对象进行深度复制，输入参数为{@code null}时返回{@code null}。<p>
	 * 该方法会对输入对象的所有字段执行深度复制操作。<br>
	 * 
	 * @param <T> 输入对象的类型
	 * @param input 要进行深度克隆的输入对象
	 * @param inputType 输入对象的类型信息
	 * @param identityFilter 用于过滤不可变类型的过滤器
	 * @return 深度克隆后的对象，如果输入为{@code null}，则返回{@code null}
	 */
	public static <T> T deepClone(T input, Type inputType, IdentityFilter identityFilter) {
		return DataProtections.deepClone(input, inputType, identityFilter);
	}
}